package brau.view.filter;

import java.io.IOException;
import java.io.InputStream;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.openddr.simpleapi.oddr.ODDRService;
import org.openddr.simpleapi.oddr.documenthandler.VocabularyHandler;
import org.openddr.simpleapi.oddr.model.ODDRHTTPEvidence;
import org.openddr.simpleapi.oddr.model.vocabulary.VocabularyProperty;
import org.w3c.ddr.simple.Evidence;
import org.w3c.ddr.simple.PropertyValue;
import org.w3c.ddr.simple.Service;
import org.w3c.ddr.simple.ServiceFactory;
import org.w3c.ddr.simple.exception.InitializationException;
import org.w3c.ddr.simple.exception.NameException;
import org.w3c.ddr.simple.exception.ValueException;
import org.xml.sax.SAXException;

public class IdentificationFilter implements Filter {

    private Service identificationService = null;
    private VocabularyHandler vocabularyHandler = new VocabularyHandler();


    public void init(FilterConfig filterConfig) throws ServletException {
		System.out.println("Initialize filter");
		Properties initializationProperties = new Properties();
		ServletContext context = filterConfig.getServletContext();
		try {
			// Carrega as informações dos dispositivos (DDR) - Arquivos xml
		    carregarArquivosXml(initializationProperties, context);  
		    
		    // instancia o servico de identificação do dispositivo
		    identificationService = ServiceFactory.newService("org.openddr.simpleapi.oddr.ODDRService",initializationProperties.getProperty(ODDRService.ODDR_VOCABULARY_IRI), initializationProperties);
		} catch (Exception ex) {
		    throw new RuntimeException(ex);
		}
	}


//  Filtro, executado cada vez que a pagina referenciada no filter do web.xml é carregada
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		
//		Cria a "Evidence" com as informações do "User-Agent" recebidas do contexto (vavegador do dispositivo requisitante"
		Evidence e = new ODDRHTTPEvidence();
		e.put("User-Agent", ((HttpServletRequest)request).getHeader("User-Agent"));

    	PropertyValue propertyValue = null;
    	
//    	Para cada propriedade relacionada do Limited Vocabulary, buscamos a informação no serviço da DDR e injetamos no cotexto da sessão
    	for (Entry<String, VocabularyProperty> entry : vocabularyHandler.getVocabulary().getProperties().entrySet()) {
    		// 	String key = entry.getKey();
	    	VocabularyProperty vProp = entry.getValue();
			try {
				propertyValue = identificationService.getPropertyValue(e, vProp.getName());
				if (propertyValue.exists()) {
					if (vProp.getType().equals("xs:string")) {
						((HttpServletRequest)request).setAttribute(vProp.getName(), propertyValue.getString());
					} else if (vProp.getType().equals("xs:boolean")) {
						((HttpServletRequest)request).setAttribute(vProp.getName(), propertyValue.getBoolean());
					} else if (vProp.getType().equals("xs:nonNegativeInteger")) {
						((HttpServletRequest)request).setAttribute(vProp.getName(), propertyValue.getInteger());
					} else if (vProp.getType().equals("xs:enumeration")) {
				    	String[] arrayEnum = propertyValue.getEnumeration();
				    	String strEnum = "";
				    	for (int i = 0; i < arrayEnum.length; i++) strEnum+=", " + arrayEnum[i];
				    	if (strEnum.length() > 0) strEnum = strEnum.substring(2);
						((HttpServletRequest)request).setAttribute(vProp.getName(), strEnum);
					} 
				}
			} catch (NameException e1) {
				e1.printStackTrace();
			} catch (ValueException e2) {
				e2.printStackTrace();
			}
	    }
//		retorna o comando para o jsf
		chain.doFilter(request, response);
    }

    public void destroy() {
    	System.out.println("Destroy Filter");
    }
    
    
    private void carregarArquivosXml(Properties initialProp, ServletContext context) throws IOException {
    	
    	initialProp.load(context.getResourceAsStream("/WEB-INF/classes/oddr.properties")); // carrega as informações de configuração da aplicação    	

    	Properties streamProperties = new Properties();
	    Set<Entry<Object, Object>> mapProp = initialProp.entrySet();
	    for (Entry<Object, Object> entry : mapProp) {
			String key = (String) entry.getKey();
			key = key.replace("path", "stream");
			String val = (String) entry.getValue();
		    if (val.toLowerCase().endsWith("xml")) {
		    	if (key.contains("patch") || key.contains("oddr.vocabulary.stream") ) {
		    		streamProperties.put(key, new InputStream[] { context.getResourceAsStream(val) } );
		    	} else {
		    		streamProperties.put(key, context.getResourceAsStream(val) );
		    		if (key.contains("limited.vocabulary")) {
	    	            try {
	    	            	// carrega em vocavularyHandler as propriedades definidas no limitedVocabulary
							parseVocabularyFromStream(vocabularyHandler, key, (InputStream) context.getResourceAsStream(val));
						} catch (InitializationException e) {
							e.printStackTrace();
						}
		    		}
		    	}
		    }
		}
	    if (streamProperties.size() > 0) initialProp.putAll(streamProperties);
    }
    
//    initializationProperties.put("oddr.ua.device.builder.stream", 								context.getResourceAsStream("/WEB-INF/classes/oddr/BuilderDataSource.xml"));
//    initializationProperties.put("oddr.ua.device.datasource.stream", 								context.getResourceAsStream("/WEB-INF/classes/oddr/DeviceDataSource.xml"));
//    initializationProperties.put("oddr.ua.browser.datasource.stream", 							context.getResourceAsStream("/WEB-INF/classes/oddr/BrowserDataSource.xml"));
//    initializationProperties.put("oddr.ua.operatingSystem.datasource.stream", 					context.getResourceAsStream("/WEB-INF/classes/oddr/OperatingSystemDataSource.xml"));
//    initializationProperties.put("ddr.vocabulary.core.stream", 									context.getResourceAsStream("/WEB-INF/classes/oddr/coreVocabulary.xml"));
//    initializationProperties.put("oddr.limited.vocabulary.stream", 								context.getResourceAsStream("/WEB-INF/classes/ens99LimitedVocabulary.xml"));
//    initializationProperties.put("oddr.vocabulary.stream", 					new InputStream[] {	context.getResourceAsStream("/WEB-INF/classes/oddr/oddrVocabulary.xml")});
//    initializationProperties.put("oddr.ua.device.builder.patch.streams", 		new InputStream[] {	context.getResourceAsStream("/WEB-INF/classes/oddr/BuilderDataSourcePatch.xml") } );
//    initializationProperties.put("oddr.ua.device.datasource.patch.streams", 	new InputStream[] {	context.getResourceAsStream("/WEB-INF/classes/oddr/DeviceDataSourcePatch.xml") } );

    private void parseVocabularyFromStream(VocabularyHandler vocabularyHandler, String prop, InputStream inputStream) throws InitializationException {
        InputStream stream = null;
        SAXParser parser = null;
        stream = inputStream;

        try {
            parser = SAXParserFactory.newInstance().newSAXParser();

        } catch (ParserConfigurationException ex) {
            throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new IllegalStateException("Can not instantiate SAXParserFactory.newInstance().newSAXParser()"));

        } catch (SAXException ex) {
            throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new IllegalStateException("Can not instantiate SAXParserFactory.newInstance().newSAXParser()"));
        }

        try {
            parser.parse(stream, vocabularyHandler);

        } catch (SAXException ex) {
            throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new RuntimeException("Can not parse document in property: " + prop));

        } catch (IOException ex) {
            throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new RuntimeException("Can not open " + prop));
        }

        try {
            stream.close();

        } catch (IOException ex) {
            Logger.getLogger(ODDRService.class.getName()).log(Level.WARNING, null, ex);
        }
        parser = null;
    }
    
}
